/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.classdiagram.tabbedproperties.composite;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.StringTokenizer;

import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.common.util.EList;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertyConstants;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;
import org.eclipse.uml2.uml.OpaqueExpression;

import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.commands.OpaqueExpressionBodyDisplayCreateCommand;
import com.cea.papyrus.core.commands.factories.DiagramElementDeleteCommandFactory;
import com.cea.papyrus.core.editor.CommandStackUtils;
import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.helper.ModelerHelper;
import com.cea.papyrus.core.preference.OpaqueLanguagesPreferenceConstants;
import com.cea.papyrus.core.tabbedproperties.composite.DecoratedTableComposite;
import com.cea.papyrus.core.tabbedproperties.composite.ISectionComposite;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagraminterchange2.di2.Di2Factory;
import com.cea.papyrus.diagraminterchange2.di2.DiagramElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.extensionpoints.lang.behaviour.BehaviourLanguage;
import com.cea.papyrus.umlutils.Element;

/**
 * 
 */
public class OpaqueExpressionBodiesComposite extends DecoratedTableComposite implements ISectionComposite {
	
	

	/**
	 * Default Constructor.
	 */
	public OpaqueExpressionBodiesComposite() {
		super("Bodies");
		this.displayButtonListener = new DisplayButtonlistener();
	}

	/**
	 * key to access UML Data from table items.
	 */ 
	private final static String BODY = "opaqueExpressionBody_data";
	
	/**
	 * 
	 */
	private final static String LANGUAGE = "opaqueExpressionLanguage_data";
	
	/**
	 * Operation for which methods are displayed.
	 */
	private OpaqueExpression umlOpaqueExpression;

	/**
	 * Add button menu.
	 */
	private Menu menu;
	
	/**
	 * 
	 */
	private Button displayButton;
	
	/**
	 * Listener for the display button.
	 */
	protected MouseListener displayButtonListener;

	
	/**
	 * The Constant IMG_DISPLAY.
	 */
	private static final Image IMG_DISPLAY	= PapyrusPlugin.getImage(PapyrusPlugin.PAPYRUS_ICONS_16x16+"ConsoleView_16x16.gif");

	/**
	 * The Constant IMG_STRING_EXPRESSION.
	 */
	private static final Image IMG_STRING_EXPRESSION	= PapyrusPlugin.getImage(PapyrusPlugin.UML_ICONS_16x16+"StringExpression.gif");
	
	/**
	 * The diagram element.
	 */
	protected DiagramElement diagramElement=null;
	
	/**
	 * The model manager.
	 */
	private ModelManager modelManager;

	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#createContent(org.eclipse.swt.widgets.Composite, org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory)
	 */
	/**
	 * 
	 * 
	 * @param factory 
	 * @param parent 
	 * 
	 * @return 
	 */
	@Override
	public Composite createContent(Composite parent, TabbedPropertySheetWidgetFactory factory) {
		Composite composite = super.createContent(parent, factory);
		upButton.setVisible(false);
		downButton.setVisible(false);
		
		// display button
		displayButton = factory.createButton(composite, "", SWT.PUSH);
		displayButton.setVisible(true);
		displayButton.setImage(IMG_DISPLAY);
		displayButton.setToolTipText("Display a body");
		
		FormData data = new FormData();
		data.right = new FormAttachment(addButton, -ITabbedPropertyConstants.HSPACE);
		data.top  = new FormAttachment(0, 0);
		displayButton.setLayoutData(data);
		displayButton.addMouseListener(displayButtonListener);

		
		// add an additional menu to the add Button
		createMenuForAddButton();
		
		if(umlOpaqueExpression==null){
			addButton.setEnabled(false);
			removeButton.setEnabled(false);
			displayButton.setEnabled(false);
		}
		
		return composite;
	}
	
	
	/**
	 * Adds an additional menu to the addButton. So user can choose which element to create
	 */
	private void createMenuForAddButton() {
		menu = new Menu (addButton);
		
		// get all behavior laguages supported by Papyrus according to languages pluggins that are present 
		BehaviourLanguage[] languages = BehaviourLanguage.getBehaviourLanguages();
		
		for(int i=0; i<languages.length; i++){
			MenuItem anItem = new MenuItem (menu, SWT.PUSH);
			anItem.setText(languages[i].langLabel);
			anItem.addSelectionListener(new AddElementSelectionListener(languages[i].langLabel));
			
		}
		
		//Get the preference  from PreferenceStore to add user defined languages
		IPreferenceStore store = PapyrusPlugin.getDefault().getPreferenceStore();		
		String userLanguages = store.getString(OpaqueLanguagesPreferenceConstants.P_LANGUAGES_NAME);
		StringTokenizer st = new StringTokenizer(userLanguages, ",");
		
		int count = st.countTokens();
		
		for(int i=0; i<count; i++){
			String newLanguage = st.nextToken();
			newLanguage = newLanguage.replace(" ", "");
			MenuItem anItem = new MenuItem (menu, SWT.PUSH);
			anItem.setText(newLanguage);
			anItem.addSelectionListener(new AddElementSelectionListener(newLanguage));			
		}

		
		addButton.setMenu (menu);
	}
	
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#setElement(com.cea.utils.Element)
	 */
	/**
	 * 
	 * 
	 * @param element 
	 */
	@Override
	public void setElement(Element element) {
		super.setElement(element);
		if(element != null)
			this.umlOpaqueExpression = (OpaqueExpression)element.getUml2Element();
		else
			this.umlOpaqueExpression = null;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#refresh()
	 */
	/**
	 * 
	 */
	@Override
	public void refresh() {
		if ( (!table.isDisposed())&&
				(!addButton.isDisposed()) &&
				(!removeButton.isDisposed()) &&
				(!displayButton.isDisposed())) {
			if (umlOpaqueExpression!=null){
				// Remove parameters from table 
				table.removeAll();

				addButton.setEnabled(true);
				removeButton.setEnabled(true);
				displayButton.setEnabled(true);

				// Read and add parameter
				Iterator<String> bodyIter = umlOpaqueExpression.getBodies().iterator();
				Iterator<String> languageIter = umlOpaqueExpression.getLanguages().iterator();
				while (bodyIter.hasNext() && languageIter.hasNext()) {
					String body = bodyIter.next();
					String language = languageIter.next();
					TableItem item = new TableItem(table, SWT.BORDER);
					item.setData(BODY, body);
					item.setData(LANGUAGE, language);
					if(isInOpaqueExpressionBodyDisplay(body, language))
						item.setImage(IMG_DISPLAY);
					else
						item.setImage(IMG_STRING_EXPRESSION);
					if(body.length()>=11)
						item.setText("{"+language+"} "+body.substring(0, 10)+"... ");
					else
						item.setText("{"+language+"} "+body);
				}
				
				/* first element selection */
				if(table.getItemCount()>0){
					table.select(0);
				}
			}


			// if there is no mapping defined, we disable buttons handling bodies of mapping
			else if(umlOpaqueExpression==null){
				table.removeAll();
				addButton.setEnabled(false);
				removeButton.setEnabled(false);
				displayButton.setEnabled(false);
			}
		}
	}
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#addButtonPressed()
	 */
	/**
	 * 
	 */
	@Override
	public void addButtonPressed() {
		menu.setVisible(true);
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#downButtonPressed()
	 */
	/**
	 * 
	 */
	@Override
	public void downButtonPressed() {
		//do nothing
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#editItem(org.eclipse.swt.widgets.TableItem)
	 */
	/**
	 * 
	 * 
	 * @param item 
	 */
	@Override
	public void editItem(TableItem item) {
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#removeButtonPressed()
	 */
	/**
	 * 
	 */
	@Override
	public void removeButtonPressed() {
		// get selection and remove it
		int selectionIndex = table.getSelectionIndex();
		if(selectionIndex != -1){ // selection not null
			String body = (String)table.getSelection()[0].getData(BODY);
			String language = (String)table.getSelection()[0].getData(LANGUAGE);
			removeOpaqueExpressionDisplay(body, language);
			umlOpaqueExpression.getBodies().remove(selectionIndex);
			umlOpaqueExpression.getLanguages().remove(selectionIndex);
		}
		refresh();
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#upButtonPressed()
	 */
	/**
	 * 
	 */
	@Override
	public void upButtonPressed() {
		//do nothing
	}
	
	
	/**
	 * 
	 * 
	 * @return 
	 */
	public Table getTable(){
		return table;
	}

	
	/**
	 * Listener for the menu items.
	 */
	public class AddElementSelectionListener implements SelectionListener {
		
		/**
		 * 
		 */
		public String language;

		/**
		 * 
		 * 
		 * @param language 
		 */
		public AddElementSelectionListener(String language) {
			this.language = language;
		}
		
		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void widgetDefaultSelected(SelectionEvent e) {
		}

		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void widgetSelected(SelectionEvent e) {
			// Append a new body and associated language
			String newBody = "";
			umlOpaqueExpression.getBodies().add(newBody);
			umlOpaqueExpression.getLanguages().add(language);
			refresh();
		}
	}
	
	/**
	 * Listener for the Display Button
	 * Specific behavior is implemented in displayButtonPressed().
	 * 
	 * @author Remi SCHNEKENBURGER
	 */
	private class DisplayButtonlistener implements MouseListener {
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseDoubleClick(MouseEvent e) {
			// do nothing
		}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseDown(MouseEvent e) {
			// do nothing
		}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseUp(MouseEvent e) {
			displayButtonPressed();
		}
	}
	
	
	/**
	 * behavior executed when display button is pressed.
	 */
	public void displayButtonPressed() {
		// If nothing selected : abort
		if (getTable().getSelection().length < 1) {
			return;
		}

		// bugfix: a selected element is not necessary a diagram element (ex: selection in the outline)
		if(getDiagramElement() == null) {
			return;
		}

		TableItem[] tableSelection = getTable().getSelection();
		for(int i=0; i<tableSelection.length; i++){
			String body = (String)tableSelection[i].getData(BODY);
			String language = (String)tableSelection[i].getData(LANGUAGE);

			if(tableSelection[i].getImage().equals(IMG_STRING_EXPRESSION)) {
				addOpaqueExpressionBodyDisplay(body, language);
			}
			else {
				// remove the body from display properties
				removeOpaqueExpressionDisplay(body, language);
			}
		}

		// Refresh
		refresh();
	}

	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#dispose()
	 */
	/**
	 * 
	 */
	public void dispose() {
		super.dispose();
		if(displayButton!=null && !displayButton.isDisposed())
			displayButton.removeMouseListener(displayButtonListener);
	}
	
	
	/************************* Bodies Display Operations **************************/
	
	/**
	 * Sets the model manager.
	 * 
	 * @param modelManager the model manager
	 */
	public void setModelManager(ModelManager modelManager){
		this.modelManager=modelManager;
	}

	/**
	 * Sets the diagram element.
	 * 
	 * @param diagramElement the diagram element
	 */
	public void setDiagramElement(DiagramElement diagramElement){
		this.diagramElement=diagramElement;
	}

	/**
	 * Returns the diagram element owning the opaque expression.
	 * 
	 * @return 
	 */
	public DiagramElement getDiagramElement() {
		return diagramElement;
	}

	/**
	 * Adds tthe body of an opaque expression to display properties of Di2 repository.
	 * 
	 * @param language 
	 * @param body 
	 */
	protected void addOpaqueExpressionBodyDisplay(String body, String language) {
		// bugfix: a selected element is not necessary a diagram element (ex: selection in the outline)
		if(getDiagramElement() == null) {
			return;
		}

		// property to add creation
		com.cea.papyrus.diagraminterchange2.di2.Property property = Di2Factory.eINSTANCE.createProperty();
		property.setKey(ModelerHelper.OPAQUE_EXPRESSION_BODY_DISPLAY);
		property.setValue("{"+language+"}\n"+body);

		// add the property
		getDiagramElement().getProperty().add(property);
		
		// command creation: display stereotype properties in active diagram
		Command command = new OpaqueExpressionBodyDisplayCreateCommand(getDiagramElement(), getDiagramElement().getContainer(), umlOpaqueExpression);

		// getCommandStack
		CommandStack stack = CommandStackUtils.getCommandStack();

		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStackUtils");
		stack.execute(command);

	}
	
	/**
	 * Removes a body form list of bodies to display.
	 * 
	 * @param language 
	 * @param body 
	 */
	protected void removeOpaqueExpressionDisplay(String body, String language){
		// bugfix: a selected element is not necessary a diagram element (ex: selection in the outline)
		if(getDiagramElement() == null) {
			return;
		}


		//Debug.debug(this, "OWNER::REMOVE = "+owner.getQualifiedName(), channel);

		EList propList = getDiagramElement().getProperty();
		ArrayList<com.cea.papyrus.diagraminterchange2.di2.Property> tempPropList = new ArrayList<com.cea.papyrus.diagraminterchange2.di2.Property>();
		for(int i=0; i<propList.size(); i++) {
			tempPropList.add(i, ((com.cea.papyrus.diagraminterchange2.di2.Property)propList.get(i)));
		}

		Iterator iter = tempPropList.iterator();

		while(iter.hasNext()) {
			com.cea.papyrus.diagraminterchange2.di2.Property prop = (com.cea.papyrus.diagraminterchange2.di2.Property)iter.next();
			if(prop.getKey().equals(ModelerHelper.OPAQUE_EXPRESSION_BODY_DISPLAY)) {
				if(prop.getValue().equals("{"+language+"}\n"+body)) {
					propList.remove(prop);
				}
			}
		}

		// count the number of opaquez expression bodies to display
		Iterator<com.cea.papyrus.diagraminterchange2.di2.Property> iterProp = getDiagramElement().getProperty().iterator();
		int count=0;
		while(iterProp.hasNext()){
			com.cea.papyrus.diagraminterchange2.di2.Property diProp = iterProp.next();
			if(diProp.getKey().equals(ModelerHelper.OPAQUE_EXPRESSION_BODY_DISPLAY)) {
				count++;
			}
		}

		// is there some properties to display?
		if(count==0 || umlOpaqueExpression==null) {
			Command command = DiagramElementDeleteCommandFactory.eINSTANCE.createCommand((GraphNode)LookForElement.getOpaqueExpressionGraphNode(umlOpaqueExpression, getDiagramElement()));

			// getCommandStack
			CommandStack stack = CommandStackUtils.getCommandStack();

			Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStackUtils");
			stack.execute(command);
		}

	}

	
	/**
	 * Removes all opaque expression bodies display.
	 * 
	 * @param umlOpaqueExpression 
	 */
	public void removeAllOpaqueExpressionDisplay(OpaqueExpression umlOpaqueExpression){
		
		Iterator<String> bodies = umlOpaqueExpression.getBodies().iterator();
		Iterator<String> languages = umlOpaqueExpression.getLanguages().iterator();
		
		while(bodies.hasNext()){
			String body = bodies.next();
			String language = languages.next();
			removeOpaqueExpressionDisplay(body, language);
		}
		
	}
	
	/**
	 * Checks wether the body is in the display list or not.
	 * 
	 * @param language 
	 * @param body 
	 * 
	 * @return true if the body is in the display list, false otherwise
	 */
	protected Boolean isInOpaqueExpressionBodyDisplay(String body, String language) {
		// bugfix: a selected element is not necessary a diagram element (ex: selection in the outline)
		if(getDiagramElement() == null) {
			return false;
		}

		EList propList = getDiagramElement().getProperty();
		Iterator iter = propList.iterator();
		while(iter.hasNext()) {
			com.cea.papyrus.diagraminterchange2.di2.Property prop = (com.cea.papyrus.diagraminterchange2.di2.Property)iter.next();
			if(prop.getKey().equals(ModelerHelper.OPAQUE_EXPRESSION_BODY_DISPLAY)) {
				if(prop.getValue().equals("{"+language+"}\n"+body)) {
					return true;
				}
			}
		}
		return false;
	}

	
	
	/******************************************************************************/
	

}
